﻿//#include "stdafx.h"
#include "XUtil.h"
#include <tchar.h>

#ifdef WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#endif


namespace XUtil {

#define MAX_PATH_LEN 256


	const char* czws = " \t\n\r\f\v";
	const wchar_t* wzws = L" \t\n\r\f\v";


#ifdef WIN32
	const std::string FILE_Separator = "\\";
#ifdef UNICODE
#define _ACCESS(fileName,accessMode) _waccess(fileName,accessMode)
#define _MKDIR(path) _wmkdir(path)
#else
#define _ACCESS(fileName,accessMode) _access(fileName,accessMode)
#define _MKDIR(path) _mkdir(path)
#endif
#else
	const std::string FILE_Separator = "/";

#define _ACCESS(fileName,accessMode) access(fileName,accessMode)
#define _MKDIR(path) mkdir(path,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
#endif

	std::string WChar2Ansi(LPCWSTR pwszSrc)
	{
		int nLen = WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, NULL, 0, NULL, NULL);

		if (nLen <= 0) return std::string("");

		char* pszDst = new char[nLen];
		if (NULL == pszDst) return std::string("");

		WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, pszDst, nLen, NULL, NULL);
		pszDst[nLen - 1] = 0;

		std::string strTemp(pszDst);
		delete[] pszDst;

		return strTemp;
	}

	std::string ws2s(std::wstring& inputws)
	{
		return WChar2Ansi(inputws.c_str());
	}

	std::wstring Ansi2WChar(LPCSTR pszSrc, int nLen)
	{
		int nSize = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszSrc, nLen, 0, 0);
		if (nSize <= 0) return NULL;

		WCHAR *pwszDst = new WCHAR[nSize + 1];
		if (NULL == pwszDst) return NULL;

		MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszSrc, nLen, pwszDst, nSize);
		pwszDst[nSize] = 0;

		if (pwszDst[0] == 0xFEFF)                    // skip Oxfeff
			for (int i = 0; i < nSize; i++)
				pwszDst[i] = pwszDst[i + 1];

		std::wstring wcharString(pwszDst);
		delete pwszDst;

		return wcharString;
	}

	std::wstring s2ws(const std::string& s)
	{
		return Ansi2WChar(s.c_str(), s.size());
	}


	void split(std::string& s, std::string& delim, std::vector<std::string>& ret)
	{
		size_t last = 0;
		size_t index = s.find_first_of(delim, last);
		while (index != std::string::npos)
		{
			ret.push_back(s.substr(last, index - last));
			last = index + 1;
			index = s.find_first_of(delim, last);
		}
		if (index - last > 0)
		{
			ret.push_back(s.substr(last, index - last));
		}
	}

	void split(std::wstring& s, std::wstring& delim, std::vector<std::wstring>& ret)
	{
		size_t last = 0;
		size_t index = s.find_first_of(delim, last);
		while (index != std::wstring::npos)
		{
			ret.push_back(s.substr(last, index - last));
			last = index + 1;
			index = s.find_first_of(delim, last);
		}
		if (index - last > 0)
		{
			ret.push_back(s.substr(last, index - last));
		}
	}

	bool IsDirectory(const TCHAR* filename)
	{
		DWORD dwAttr = ::GetFileAttributes(filename);  //得到文件属性

		if (dwAttr == 0xFFFFFFFF)    // 文件或目录不存在
			return false;
		else if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)  // 如果是目录
			return true;
		else
			return false;
	}

	bool haveNoAscii(const char* pstr, int len)
	{
		if (!pstr)
			return false;

		for (int i=0; i<len; i++)
		{
			char ch = pstr[i];
			if (isprint(ch))
				;
			else
				return true;
		}

		return false;
	}
	//==================================================================
	void hex2asc(unsigned char *hex, int hexlength, unsigned char *asc)
	{
		for (int i = 0; i < hexlength; i++)
		{
			sprintf((char*)asc + 2 * i, "%02X", hex[i]);
		}
		asc[hexlength * 2] = 0;
	}

	unsigned char t[256] = {
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
		0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	};

	/*
	* s    : 要转换的字符串，如 “123456AbCdEf”。
	* h    : 要存放转换结果的字节数组。
	* slen : 要转换的字符串长度, -1时自动获取
	* 返回：写入 h 的元素个数
	*/
	int str2hex(const char *s, unsigned char *h, int slen/* = -1*/)
	{
		int i, n;
		unsigned char *p;

		p = h;
		n = slen;
		if (n == -1)
			n = strlen(s);

		if (n % 2) {
			h[0] = t[(unsigned char)s[0]];
			++p;
			++s;
		}

		for (i = 0; i < n; i = i + 2) {
			*(p++) = (t[(unsigned char)s[i]] << 4) | t[(unsigned char)s[i + 1]];
		}

		return (n + 1) / 2;
	}

	std::string hex2str(const unsigned char *pData, int size, char chSpan, int lineSize)
	{
		std::string dumpstr;
		if (pData && size > 0) {
			char buf[16] = { 0 };
			for (int m = 0; m < size; m++) {
				if (lineSize > 0) {
					if (m > 0 && !(m % lineSize))
						dumpstr += "\n";

					if ((0 == (m % 8)) && ((m / 8) % (lineSize / 8)))
						dumpstr += "- ";
				}

				sprintf(buf, "%02X%c", pData[m], chSpan);
				dumpstr += buf;
			}
		}
		return dumpstr;
	}

	std::string toString(UINT uV, int alignLen, int base)
	{
		char buffer[32] = "";
		if (base == 16)
		{
			if (alignLen)
			{
				char fmt[16] = {0};
				sprintf(fmt, "0x%%0%dX", alignLen);
				sprintf(buffer, fmt, uV);
			}
			else
				sprintf(buffer, "0x%X", uV);
			return buffer;
		}
		return _itoa(uV, buffer, base);
	}

	std::string toString64(LONGLONG uV, int alignLen, int base)
	{
		char buffer[32] = "";
		if (base == 16)
		{
			if (alignLen)
			{
				char fmt[16] = { 0 };
				sprintf(fmt, "0x%%0%dllX", alignLen);
				sprintf(buffer, fmt, uV);
			}
			else
				sprintf(buffer, "0x%llX", uV);
			return buffer;
		}
		return _i64toa(uV, buffer, base);
	}

	std::string toStringF(float uV)
	{
		char buffer[256] = "";
		sprintf(buffer, "%.4g", uV);
		return buffer;
	}

	xstring GetProgramPath(HINSTANCE hModule)
	{
		TCHAR szPath[MAX_PATH + 1];

		GetModuleFileName(hModule, szPath, MAX_PATH);

		return xstring(szPath);
	}


	xstring GetProgramDir(HINSTANCE hModule)
	{
		TCHAR szPath[MAX_PATH + 1];

		GetModuleFileName(hModule, szPath, MAX_PATH);
		TCHAR *pszLast = _tcsrchr(szPath, '\\');
		if (pszLast)
			*pszLast = '\0';

		return xstring(szPath);
	}

#ifdef WIN32
	// 过滤器示例 TEXT("kt Flie\0*.kt;\0所有文件\0*.*\0\0");
	xstring ShowOpenfile(LPCTSTR filter, LPCTSTR title, LPCTSTR initDir)
	{
		OPENFILENAME ofn = { 0 };
		TCHAR strFilename[MAX_PATH] = { 0 };//用于接收文件名 

		ofn.lStructSize = sizeof(OPENFILENAME);//结构体大小  
		ofn.hwndOwner = NULL;//拥有着窗口句柄，为NULL表示对话框是非模态的，实际应用中一般都要有这个句柄  
		ofn.lpstrFilter = filter;	//设置过滤  
		ofn.nFilterIndex = 1;//过滤器索引  
		ofn.lpstrFile = strFilename;//接收返回的文件名，注意第一个字符需要为NULL  
		ofn.nMaxFile = sizeof(strFilename);//缓冲区长度  
		ofn.lpstrInitialDir = initDir;	//初始目录为默认  
		ofn.lpstrTitle = title;	//使用系统默认标题留空即可  
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;//文件、目录必须存在，隐藏只读选项  
		GetOpenFileName(&ofn);
		
		return strFilename;
	}

	// 注释：多字节包括GBK和UTF-8
// Len为目标缓冲区长度
	std::string GBK2UTF8(char *szGbk)
	{
		std::string retstr;
		// 先将多字节GBK（CP_ACP或ANSI）转换成宽字符UTF-16
		// 得到转换后，所需要的内存字符数
		int n = MultiByteToWideChar(CP_ACP, 0, szGbk, -1, NULL, 0);
		// 字符数乘以 sizeof(WCHAR) 得到字节数
		WCHAR *str1 = new WCHAR[sizeof(WCHAR) * n];
		// 转换
		MultiByteToWideChar(CP_ACP,  // MultiByte的代码页Code Page
							0,            //附加标志，与音标有关
							szGbk,        // 输入的GBK字符串
							-1,           // 输入字符串长度，-1表示由函数内部计算
							str1,         // 输出
							n             // 输出所需分配的内存
		);

		// 再将宽字符（UTF-16）转换多字节（UTF-8）
		n = WideCharToMultiByte(CP_UTF8, 0, str1, -1, NULL, 0, NULL, NULL);
		char* szUtf8 = new char[n+1];
		WideCharToMultiByte(CP_UTF8, 0, str1, -1, szUtf8, n, NULL, NULL);
		delete[] str1;
		str1 = NULL;
		retstr = szUtf8;
		delete[] szUtf8;
		return retstr;
	}

	//UTF-8 GBK
	std::string UTF82GBK(char *szUtf8)
	{
		std::string retstr;
		int n = MultiByteToWideChar(CP_UTF8, 0, szUtf8, -1, NULL, 0);
		WCHAR * wszGBK = new WCHAR[sizeof(WCHAR) * n];
		memset(wszGBK, 0, sizeof(WCHAR) * n);
		MultiByteToWideChar(CP_UTF8, 0, szUtf8, -1, wszGBK, n);

		n = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);

		char* szGbk = new char[n+1];
		WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGbk, n, NULL, NULL);

		delete[]wszGBK;
		wszGBK = NULL;
		retstr = szGbk;
		delete[] szGbk;
		return retstr;
	}
#endif

	void MakeLower(std::string &strsrc) {
		std::transform(strsrc.begin(), strsrc.end(), strsrc.begin(), ::tolower);
	}

	void MakeUpper(std::string &strsrc) {
		std::transform(strsrc.begin(), strsrc.end(), strsrc.begin(), ::toupper);
	}

	void StringReplace(std::string &strBase, std::string strSrc, std::string strDes) {
		std::string::size_type pos = 0;
		std::string::size_type srcLen = strSrc.size();
		std::string::size_type desLen = strDes.size();
		pos = strBase.find(strSrc, pos);
		while ((pos != std::string::npos)) {
			strBase.replace(pos, srcLen, strDes);
			pos = strBase.find(strSrc, (pos + desLen));
		}
	}

	std::string GetNameWithDir(std::string path, int back_level) {
		std::string retstr = path;
		if (back_level > 0) {
			int pos = path.rfind(FILE_Separator);
			while (pos != std::string::npos) {
				pos = path.rfind(FILE_Separator, pos - 1);
				if (--back_level <= 0) {
					retstr = path.substr(pos + 1);
					StringReplace(retstr, FILE_Separator, "_");
					break;
				}
			}
		}

		return retstr;
	}

	// 从左到右依次判断文件夹是否存在,不存在就创建
	// example: /home/root/mkdir/1/2/3/4/
	// 注意:最后一个如果是文件夹的话,需要加上 '\' 或者 '/'
	int32_t mkDirs(const xstring &directoryPath)
	{
		uint32_t dirPathLen = directoryPath.length();
		if (dirPathLen > MAX_PATH_LEN)
		{
			return -1;
		}
		TCHAR tmpDirPath[MAX_PATH_LEN] = { 0 };
		for (uint32_t i = 0; i < dirPathLen; ++i)
		{
			tmpDirPath[i] = directoryPath[i];
			if (tmpDirPath[i] == '\\' || tmpDirPath[i] == '/')
			{
				if (_ACCESS(tmpDirPath, 0) != 0)
				{
					int32_t ret = _MKDIR(tmpDirPath);
					if (ret != 0)
					{
						return ret;
					}
				}
			}
		}
		return 0;
	}

	bool fileIsExist(const xstring &_path)
	{
		return (_ACCESS(_path.c_str(), 0) != -1);
	}

#ifdef WIN32
	inline DWORD GetAddrModuleName(PVOID addr, char* szModule, int len)
	{
		// Get information about the provided address
		MEMORY_BASIC_INFORMATION mbi;
		if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi)))
			return 0;

		// Get module
		DWORD hMod = (DWORD)mbi.AllocationBase;
		if (!GetModuleFileNameA((HMODULE)hMod, szModule, len))
			return 0;

		return ((DWORD)addr - hMod);
	}

	std::string GetAddrModuleFileTitle(PVOID addr, int *ret/* = NULL*/)
	{
		char filepath[MAX_PATH] = "";
		char buff[16] = "";
		DWORD offset = GetAddrModuleName(addr, filepath, MAX_PATH - 1);
		if (offset)
		{
			if (ret)
				*ret = 1;

			sprintf(buff, "!+%X", offset);

			std::string strPath;
			strPath = filepath;
			int nPos = strPath.rfind('\\');
			return strPath.substr(nPos + 1) + buff;

		}
		sprintf(buff, "%X", (unsigned int)addr);
		return buff;
	}

	DWORD GetAddrOffset(DWORD addr)
	{
		MEMORY_BASIC_INFORMATION mbi;
		if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi)))
			return addr;

		// Get module
		DWORD hMod = (DWORD)mbi.AllocationBase;
		return (addr - hMod);
	}

	std::string GetCallStack(DWORD _EBP, int nDeep/* = 3*/)
	{
		int i = 0;
		std::string StrResult;
		while (i++ < nDeep)
		{
			if (IsBadReadPtr((void*)(_EBP + 4), 4))
				break;
			int ret = 0;
			std::string moduleInfo = GetAddrModuleFileTitle((void*)*(PDWORD)(_EBP + 4), &ret);
			if (ret != 1)
				break;

			if (!StrResult.empty())
				StrResult += "<--";
			StrResult += moduleInfo;

			_EBP = *(PDWORD)_EBP;
		}

		return StrResult;
	}

	DWORD SafeGet(void* addr, int size/* = 4*/)
	{
		if (IsBadReadPtr(addr, size))
			return 0;

		switch (size)
		{
		case 1:
			return *(PBYTE)(addr);
			break;
		case 2:
			return *(PWORD)(addr);
			break;
		case 4:
			return *(PDWORD)(addr);
			break;
		}

		return 0;
	}


	//可同时处理目录和文件:path可以是路径，也可以是文件名，或者文件通配符  
	void trav_dir(xstring path, const std::function<int(LPCTSTR)> &process_func) {
		//取路径名最后一个"\"之前的部分,包括"\\"  
		xstring prefix = path.substr(0, path.find_last_of('\\') + 1);

		WIN32_FIND_DATA FindFileData;
		HANDLE hFind = ::FindFirstFile(path.c_str(), &FindFileData);
		if (INVALID_HANDLE_VALUE == hFind)
		{
			return;
		}
		while (TRUE)
		{
			//目录  
			if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				//不是当前目录，也不是父目录  
				if (!_tcscmp(FindFileData.cFileName, _T("."))
					&& !_tcscmp(FindFileData.cFileName, _T("..")))
				{
					//查找下一级目录  
					trav_dir(prefix + FindFileData.cFileName + _T("\\") + _T("*.*"), process_func);
				}
			}
			else
			{	//文件  
				process_func((prefix + FindFileData.cFileName).c_str());
			}
			if (!FindNextFile(hFind, &FindFileData))
				break;
		}
		FindClose(hFind);
	}

#endif

};	// namespace XUtil
